Mélyreható elemzés a JavaScript WeakRef és FinalizationRegistry használatáról egy memóriahatékony Observer minta létrehozásához. Tanulja meg a memóriaszivárgások megelőzését nagy méretű alkalmazásokban.
JavaScript WeakRef Observer Minta: Memóriatudatos Eseményrendszerek Építése
A modern webfejlesztés világában az Egyoldalas Alkalmazások (Single Page Applications, SPAs) váltak a dinamikus és reszponzív felhasználói élmények létrehozásának szabványává. Ezek az alkalmazások gyakran hosszú ideig futnak, komplex állapotokat kezelnek és számtalan felhasználói interakciót dolgoznak fel. Ennek a hosszú élettartamnak azonban van egy rejtett ára: a memóriaszivárgások megnövekedett kockázata. A memóriaszivárgás, amikor egy alkalmazás olyan memóriát foglal le, amire már nincs szüksége, idővel ronthatja a teljesítményt, ami lassuláshoz, böngésző összeomlásokhoz és rossz felhasználói élményhez vezet. Ezen szivárgások egyik leggyakoribb forrása egy alapvető tervezési mintában rejlik: az Observer mintában.
Az Observer minta az eseményvezérelt architektúra egyik sarokköve, amely lehetővé teszi, hogy objektumok (megfigyelők) feliratkozzanak egy központi objektumra (a tárgyra) és frissítéseket kapjanak tőle. Elegáns, egyszerű és hihetetlenül hasznos. A klasszikus megvalósításának azonban van egy kritikus hibája: a tárgy erős referenciákat tart fenn a megfigyelőire. Ha egy megfigyelőre már nincs szükség az alkalmazás többi részében, de a fejlesztő elfelejti explicit módon leiratkoztatni a tárgyról, soha nem fogja a szemétgyűjtő (garbage collector) felszabadítani. A memóriában reked, mint egy szellem, amely kísérti az alkalmazás teljesítményét.
Itt jön képbe a modern JavaScript az ECMAScript 2021 (ES12) funkcióival, amelyek hatékony megoldást kínálnak. A WeakRef és a FinalizationRegistry kihasználásával egy olyan memóriatudatos Observer mintát építhetünk, amely automatikusan takarít maga után, megelőzve ezeket a gyakori szivárgásokat. Ez a cikk mélyrehatóan foglalkozik ezzel a haladó technikával. Feltárjuk a problémát, megértjük az eszközöket, nulláról felépítünk egy robusztus implementációt, és megvitatjuk, mikor és hol érdemes ezt a hatékony mintát alkalmazni a globális alkalmazásokban.
A Fő Probléma Megértése: A Klasszikus Observer Minta és Memórialábnyoma
Mielőtt értékelni tudnánk a megoldást, teljes mértékben meg kell értenünk a problémát. Az Observer minta, más néven Kiadó-Feliratkozó (Publisher-Subscriber) minta, a komponensek szétválasztására szolgál. Egy Tárgy (vagy Kiadó) listát vezet a függőségeiről, amelyeket Megfigyelőknek (vagy Feliratkozóknak) nevezünk. Amikor a Tárgy állapota megváltozik, automatikusan értesíti az összes Megfigyelőjét, általában egy meghatározott metódus, például az update() meghívásával.
Nézzünk egy egyszerű, klasszikus megvalósítást JavaScriptben.
Egy Egyszerű Tárgy (Subject) Implementáció
Itt egy alap Tárgy (Subject) osztály. Vannak metódusai a feliratkozásra, leiratkozásra és a megfigyelők értesítésére.
class ClassicSubject {
constructor() {
this.observers = [];
}
subscribe(observer) {
this.observers.push(observer);
console.log(`${observer.name} has subscribed.`);
}
unsubscribe(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
console.log(`${observer.name} has unsubscribed.`);
}
notify(data) {
console.log('Notifying observers...');
this.observers.forEach(observer => observer.update(data));
}
}
És itt van egy egyszerű Observer osztály, amely feliratkozhat a Tárgyra.
class Observer {
constructor(name) {
this.name = name;
}
update(data) {
console.log(`${this.name} received data: ${data}`);
}
}
A Rejtett Veszély: Megmaradó Referenciák
Ez a megvalósítás tökéletesen működik, amíg gondosan kezeljük a megfigyelőink életciklusát. A probléma akkor merül fel, amikor nem. Vegyünk egy gyakori forgatókönyvet egy nagy alkalmazásban: egy hosszú életű globális adattár (a Tárgy) és egy ideiglenes UI komponens (a Megfigyelő), amely megjeleníti az adatok egy részét.
Szimuláljuk ezt a forgatókönyvet:
const dataStore = new ClassicSubject();
function manageUIComponent() {
let chartComponent = new Observer('ChartComponent');
dataStore.subscribe(chartComponent);
// A komponens elvégzi a dolgát...
// Most a felhasználó elnavigál, és a komponensre már nincs szükség.
// Egy fejlesztő elfelejtheti hozzáadni a takarító kódot:
// dataStore.unsubscribe(chartComponent);
chartComponent = null; // Elengedjük a referenciánkat a komponensre.
}
manageUIComponent();
// Később az alkalmazás életciklusában...
dataStore.notify('New data available!');
A `manageUIComponent` függvényben létrehozunk egy `chartComponent`-et és feliratkoztatjuk a `dataStore`-unkra. Később a `chartComponent`-et `null`-ra állítjuk, jelezve, hogy végeztünk vele. Azt várnánk, hogy a JavaScript szemétgyűjtő (GC) látja, hogy nincs több referencia erre az objektumra, és visszaveszi a memóriáját.
De van még egy referencia! A `dataStore.observers` tömb továbbra is tartalmaz egy közvetlen, erős referenciát a `chartComponent` objektumra. Emiatt az egyetlen megmaradt referencia miatt a szemétgyűjtő nem tudja felszabadítani a memóriát. A `chartComponent` objektum és az általa birtokolt összes erőforrás a `dataStore` teljes élettartama alatt a memóriában marad. Ha ez ismétlődően megtörténik – például minden alkalommal, amikor egy felhasználó megnyit és bezár egy modális ablakot – az alkalmazás memóriahasználata a végtelenségig nőni fog. Ez egy klasszikus memóriaszivárgás.
Egy Új Remény: A WeakRef és a FinalizationRegistry Bemutatása
Az ECMAScript 2021 két új funkciót vezetett be, amelyeket kifejezetten az ilyen típusú memóriakezelési kihívások kezelésére terveztek: a `WeakRef`-et és a `FinalizationRegistry`-t. Ezek haladó eszközök, és óvatosan kell őket használni, de a mi Observer minta problémánkra tökéletes megoldást jelentenek.
Mi az a WeakRef?
A `WeakRef` objektum egy gyenge referenciát tart egy másik objektumra, amelyet céljának (target) nevezünk. A legfőbb különbség a gyenge referencia és a normál (erős) referencia között a következő: egy gyenge referencia nem akadályozza meg a cél objektumának szemétgyűjtését.
Ha egy objektumra csak gyenge referenciák mutatnak, a JavaScript motor szabadon megsemmisítheti az objektumot és visszanyerheti a memóriáját. Pontosan erre van szükségünk az Observer problémánk megoldásához.
A `WeakRef` használatához létre kell hozni egy példányt belőle, átadva a cél objektumot a konstruktornak. A cél objektum későbbi eléréséhez a `deref()` metódust kell használni.
let targetObject = { id: 42 };
const weakRefToObject = new WeakRef(targetObject);
// Az objektum elérése:
const retrievedObject = weakRefToObject.deref();
if (retrievedObject) {
console.log(`Object is still alive: ${retrievedObject.id}`); // Kimenet: Object is still alive: 42
} else {
console.log('Object has been garbage collected.');
}
A kulcsfontosságú rész az, hogy a `deref()` visszaadhat `undefined`-et. Ez akkor történik, ha a `targetObject`-et a szemétgyűjtő eltávolította, mert már nem léteznek rá erős referenciák. Ez a viselkedés a memóriatudatos Observer mintánk alapja.
Mi az a FinalizationRegistry?
Bár a `WeakRef` lehetővé teszi egy objektum begyűjtését, nem ad tiszta módot arra, hogy tudjuk, mikor gyűjtötték be. Időnként ellenőrizhetnénk a `deref()`-et és eltávolíthatnánk az `undefined` eredményeket a megfigyelői listánkból, de ez nem hatékony. Itt jön képbe a `FinalizationRegistry`.
A `FinalizationRegistry` lehetővé teszi egy visszahívási (callback) függvény regisztrálását, amely azután hívódik meg, hogy egy regisztrált objektumot a szemétgyűjtő eltávolított. Ez egy mechanizmus a post-mortem takarításra.
Így működik:
- Létrehozol egy regisztert egy takarító visszahívással.
- `register()`-elsz egy objektumot a regiszterrel. Megadhatsz egy `heldValue`-t is, ami egy adatdarab, amit a visszahívásod kap meg, amikor az objektumot begyűjtik. Ez a `heldValue` nem lehet közvetlen referencia magára az objektumra, mert az meghiúsítaná a célt!
// 1. A regiszter létrehozása a takarító visszahívással
const registry = new FinalizationRegistry(heldValue => {
console.log(`An object has been garbage collected. Cleanup token: ${heldValue}`);
});
(function() {
let objectToTrack = { name: 'Temporary Data' };
let cleanupToken = 'temp-data-123';
// 2. Az objektum regisztrálása és egy token megadása a takarításhoz
registry.register(objectToTrack, cleanupToken);
// Az objectToTrack itt kikerül a hatókörből
})();
// Valamikor a jövőben, miután a GC lefutott, a konzol ezt fogja kiírni:
// "An object has been garbage collected. Cleanup token: temp-data-123"
Fontos Figyelmeztetések és Legjobb Gyakorlatok
Mielőtt belemerülnénk a megvalósításba, kritikus fontosságú megérteni ezeknek az eszközöknek a természetét. A szemétgyűjtő viselkedése nagymértékben implementációfüggő és nem determinisztikus. Ez azt jelenti:
- Nem tudod megjósolni, mikor fog egy objektumot begyűjteni. Ez lehet másodpercekkel, percekkel, vagy akár még tovább is azután, hogy elérhetetlenné válik.
- Nem hagyatkozhatsz arra, hogy a `FinalizationRegistry` visszahívások időben vagy предсказуемо lefutnak. Ezek takarításra valók, nem kritikus alkalmazáslogikára.
- A `WeakRef` és a `FinalizationRegistry` túlzott használata nehezebbé teheti a kód megértését. Mindig részesítsd előnyben az egyszerűbb megoldásokat (mint az explicit `unsubscribe` hívásokat), ha az objektumok életciklusai egyértelműek és kezelhetőek.
Ezek a funkciók leginkább olyan helyzetekre alkalmasak, ahol az egyik objektum (a megfigyelő) életciklusa valóban független és ismeretlen a másik objektum (a tárgy) számára.
A `WeakRefObserver` Minta Felépítése: Lépésről Lépésre
Most kombináljuk a `WeakRef`-et és a `FinalizationRegistry`-t egy memóriabiztos `WeakRefSubject` osztály felépítéséhez.
1. Lépés: A `WeakRefSubject` Osztály Szerkezete
Az új osztályunk a megfigyelőkre mutató `WeakRef`-eket fog tárolni a közvetlen referenciák helyett. Továbbá rendelkezni fog egy `FinalizationRegistry`-vel, amely a megfigyelői lista automatikus takarítását kezeli.
class WeakRefSubject {
constructor() {
this.observers = new Set(); // Set használata a könnyebb eltávolítás érdekében
// A finalizer visszahívás. Megkapja a regisztráció során megadott értéket.
// A mi esetünkben ez az érték maga a WeakRef példány lesz.
this.cleanupRegistry = new FinalizationRegistry(weakRefObserver => {
console.log('Finalizer: An observer has been garbage collected. Cleaning up...');
this.observers.delete(weakRefObserver);
});
}
}
Egy `Set`-et használunk egy `Array` helyett a megfigyelői listánkhoz. Ennek az az oka, hogy egy elem törlése egy `Set`-ből sokkal hatékonyabb (O(1) átlagos időkomplexitás), mint egy `Array` szűrése (O(n)), ami hasznos lesz a takarítási logikánkban.
2. Lépés: A `subscribe` Metódus
A `subscribe` metódus az, ahol a varázslat kezdődik. Amikor egy megfigyelő feliratkozik, a következőket fogjuk tenni:
- Létrehozunk egy `WeakRef`-et, amely a megfigyelőre mutat.
- Hozzáadjuk ezt a `WeakRef`-et az `observers` set-ünkhöz.
- Regisztráljuk az eredeti megfigyelő objektumot a `FinalizationRegistry`-nkkel, az újonnan létrehozott `WeakRef`-et használva `heldValue`-ként.
// A WeakRefSubject osztályon belül...
subscribe(observer) {
// Ellenőrizzük, hogy létezik-e már megfigyelő ezzel a referenciával
for (const ref of this.observers) {
if (ref.deref() === observer) {
console.warn('Observer already subscribed.');
return;
}
}
const weakRefObserver = new WeakRef(observer);
this.observers.add(weakRefObserver);
// Regisztráljuk az eredeti megfigyelő objektumot. Amikor begyűjtésre kerül,
// a finalizer a `weakRefObserver` argumentummal fog meghívódni.
this.cleanupRegistry.register(observer, weakRefObserver);
console.log('An observer has subscribed.');
}
Ez a beállítás egy okos hurkot hoz létre: a tárgy gyenge referenciát tart a megfigyelőre. A regiszter (belsőleg) erős referenciát tart a megfigyelőre, amíg az szemétgyűjtésre nem kerül. Miután begyűjtötték, a regiszter visszahívása aktiválódik a gyenge referencia példánnyal, amit aztán felhasználhatunk az `observers` set-ünk takarítására.
3. Lépés: Az `unsubscribe` Metódus
Még az automatikus takarítás mellett is biztosítanunk kell egy manuális `unsubscribe` metódust olyan esetekre, ahol determinisztikus eltávolításra van szükség. Ennek a metódusnak meg kell találnia a megfelelő `WeakRef`-et a set-ünkben, mindegyiket dereferenciálva és összehasonlítva azzal a megfigyelővel, amelyet el akarunk távolítani.
// A WeakRefSubject osztályon belül...
unsubscribe(observer) {
let refToRemove = null;
for (const weakRef of this.observers) {
if (weakRef.deref() === observer) {
refToRemove = weakRef;
break;
}
}
if (refToRemove) {
this.observers.delete(refToRemove);
// FONTOS: Le kell iratkoznunk a finalizerről is
// hogy megakadályozzuk a visszahívás felesleges későbbi lefutását.
this.cleanupRegistry.unregister(observer);
console.log('An observer has unsubscribed manually.');
}
}
4. Lépés: A `notify` Metódus
A `notify` metódus végigmegy a `WeakRef`-ek set-jén. Mindegyiknél megpróbálja `deref()`-elni, hogy megkapja a tényleges megfigyelő objektumot. Ha a `deref()` sikeres, az azt jelenti, hogy a megfigyelő még él, és meghívhatjuk az `update` metódusát. Ha `undefined`-et ad vissza, a megfigyelőt begyűjtötték, és egyszerűen figyelmen kívül hagyhatjuk. A `FinalizationRegistry` végül eltávolítja a hozzá tartozó `WeakRef`-et a set-ből.
// A WeakRefSubject osztályon belül...
notify(data) {
console.log('Notifying observers...');
for (const weakRefObserver of this.observers) {
const observer = weakRefObserver.deref();
if (observer) {
// A megfigyelő még él
observer.update(data);
} else {
// A megfigyelőt a szemétgyűjtő eltávolította.
// A FinalizationRegistry fogja kezelni ennek a weakRef-nek a Set-ből való eltávolítását.
console.log('Found a dead observer reference during notification.');
}
}
}
Mindent Összerakva: Egy Gyakorlati Példa
Térjünk vissza az UI komponens forgatókönyvünkhöz, de ezúttal az új `WeakRefSubject`-ünket használva. Az egyszerűség kedvéért ugyanazt az `Observer` osztályt fogjuk használni, mint korábban.
// Ugyanaz az egyszerű Observer osztály
class Observer {
constructor(name) {
this.name = name;
}
update(data) {
console.log(`${this.name} received data: ${data}`);
}
}
Most hozzunk létre egy globális adatszolgáltatást és szimuláljunk egy ideiglenes UI widgetet.
const globalDataService = new WeakRefSubject();
function createAndDestroyWidget() {
console.log('--- Creating and subscribing new widget ---');
let chartWidget = new Observer('RealTimeChartWidget');
globalDataService.subscribe(chartWidget);
// A widget most aktív, és értesítéseket fog kapni
globalDataService.notify({ price: 100 });
console.log('--- Destroying widget (releasing our reference) ---');
// Végeztünk a widgettel. A referenciánkat null-ra állítjuk.
// NEM kell meghívnunk az unsubscribe()-ot.
chartWidget = null;
}
createAndDestroyWidget();
console.log('--- After widget destruction, before garbage collection ---');
globalDataService.notify({ price: 105 });
A `createAndDestroyWidget()` futtatása után a `chartWidget` objektumra már csak a `globalDataService`-en belüli `WeakRef` hivatkozik. Mivel ez egy gyenge referencia, az objektum most már alkalmas a szemétgyűjtésre.
Amikor a szemétgyűjtő végül lefut (amit nem tudunk megjósolni), két dolog fog történni:
- A `chartWidget` objektum eltávolításra kerül a memóriából.
- A `FinalizationRegistry`-nk visszahívása aktiválódik, ami eltávolítja a most már halott `WeakRef`-et a `globalDataService.observers` set-ből.
Ha a szemétgyűjtő lefutása után újra meghívjuk a `notify`-t, a `deref()` hívás `undefined`-et ad vissza, a halott megfigyelőt kihagyjuk, és az alkalmazás hatékonyan fut tovább memóriaszivárgások nélkül. Sikeresen szétválasztottuk a megfigyelő életciklusát a tárgyétól.
Mikor Használjuk (és Mikor Kerüljük) a `WeakRefObserver` Mintát
Ez a minta hatékony, de nem egy csodaszer. Bonyolultságot vezet be és nem determinisztikus viselkedésre támaszkodik. Kulcsfontosságú tudni, mikor ez a megfelelő eszköz a feladathoz.
Ideális Felhasználási Esetek
- Hosszú Életű Tárgyak és Rövid Életű Megfigyelők: Ez a kanonikus felhasználási eset. Egy globális szolgáltatás, adattár vagy gyorsítótár (a tárgy), amely az alkalmazás teljes életciklusa alatt létezik, miközben számos UI komponens, ideiglenes worker vagy plugin (a megfigyelők) gyakran jön létre és semmisül meg.
- Gyorsítótárazási Mechanizmusok: Képzelj el egy gyorsítótárat, amely egy komplex objektumot valamilyen számított eredményhez rendel. Használhatsz egy `WeakRef`-et a kulcs objektumhoz. Ha az eredeti objektumot a szemétgyűjtő eltávolítja az alkalmazás többi részéből, a `FinalizationRegistry` automatikusan kitakaríthatja a megfelelő bejegyzést a gyorsítótáradból, megelőzve a memória felduzzadását.
- Plugin és Bővítmény Architektúrák: Ha egy olyan alaprendszert építesz, amely lehetővé teszi harmadik féltől származó modulok számára, hogy eseményekre iratkozzanak fel, egy `WeakRefObserver` használata egy ellenálló réteget ad hozzá. Megakadályozza, hogy egy rosszul megírt plugin, amely elfelejt leiratkozni, memóriaszivárgást okozzon az alapalkalmazásodban.
- Adatok DOM Elemekhez Való Rendelése: Deklaratív keretrendszer nélküli esetekben előfordulhat, hogy valamilyen adatot egy DOM elemhez szeretnél társítani. Ha ezt egy map-ben tárolod a DOM elemmel mint kulccsal, memóriaszivárgást okozhatsz, ha az elemet eltávolítják a DOM-ból, de még mindig a map-edben van. A `WeakMap` itt jobb választás, de az elv ugyanaz: az adatok életciklusát az elem életciklusához kell kötni, nem fordítva.
Mikor Maradjunk a Klasszikus Observernél
- Szorosan Összekapcsolt Életciklusok: Ha a tárgy és a megfigyelői mindig együtt jönnek létre és semmisülnek meg, vagy ugyanabban a hatókörben, a `WeakRef` többletköltsége és bonyolultsága felesleges. Egy egyszerű, explicit `unsubscribe()` hívás olvashatóbb és kiszámíthatóbb.
- Teljesítménykritikus „Hot Path”-ek: A `deref()` metódusnak van egy kicsi, de nem nulla teljesítményköltsége. Ha másodpercenként több százszor értesítesz több ezer megfigyelőt (pl. egy játékciklusban vagy nagyfrekvenciás adatvizualizációban), a klasszikus implementáció közvetlen referenciákkal gyorsabb lesz.
- Egyszerű Alkalmazások és Szkriptek: Kisebb alkalmazásoknál vagy szkripteknél, ahol az alkalmazás élettartama rövid és a memóriakezelés nem jelentős probléma, a klasszikus minta egyszerűbb megvalósítani és megérteni. Ne adj hozzá bonyolultságot, ahol nincs rá szükség.
- Amikor Determinisztikus Takarítás Szükséges: Ha egy műveletet pontosan abban a pillanatban kell végrehajtanod, amikor egy megfigyelőt leválasztanak (pl. egy számláló frissítése, egy specifikus hardver erőforrás felszabadítása), akkor muszáj egy manuális `unsubscribe()` metódust használnod. A `FinalizationRegistry` nem determinisztikus természete alkalmatlanná teszi olyan logikára, amelynek kiszámíthatóan kell lefutnia.
Tágabb Következmények a Szoftverarchitektúrára
A gyenge referenciák bevezetése egy olyan magas szintű nyelvbe, mint a JavaScript, a platform érését jelzi. Lehetővé teszi a fejlesztők számára, hogy kifinomultabb és ellenállóbb rendszereket építsenek, különösen a hosszú ideig futó alkalmazások esetében. Ez a minta egy elmozdulást ösztönöz az architekturális gondolkodásban:
- Valódi Szétválasztás: Olyan szintű szétválasztást tesz lehetővé, amely túlmutat a puszta interfészen. Most már maguknak a komponenseknek az életciklusait is szétválaszthatjuk. A tárgynak már nem kell tudnia semmit arról, hogy a megfigyelői mikor jönnek létre vagy semmisülnek meg.
- Tervezés Általi Ellenállóképesség: Segít olyan rendszereket építeni, amelyek ellenállóbbak a programozói hibákkal szemben. Egy elfelejtett `unsubscribe()` hívás egy gyakori hiba, amelyet nehéz lehet lenyomozni. Ez a minta ezt a teljes hibakategóriát enyhíti.
- Keretrendszer- és Könyvtárszerzők Támogatása: Azok számára, akik keretrendszereket, könyvtárakat vagy platformokat építenek más fejlesztők számára, ezek az eszközök felbecsülhetetlenek. Lehetővé teszik olyan robusztus API-k létrehozását, amelyek kevésbé vannak kitéve a könyvtár fogyasztói általi helytelen használatnak, ami összességében stabilabb alkalmazásokhoz vezet.
Konklúzió: Egy Hatékony Eszköz a Modern JavaScript Fejlesztő Számára
A klasszikus Observer minta a szoftvertervezés egyik alapvető építőköve, de az erős referenciákra való támaszkodása régóta a finom és frusztráló memóriaszivárgások forrása a JavaScript alkalmazásokban. Az ES2021-ben megjelent `WeakRef` és `FinalizationRegistry` révén most már rendelkezésünkre állnak az eszközök ennek a korlátnak a leküzdésére.
Eljutottunk a megmaradó referenciák alapvető problémájának megértésétől egy teljes, memóriatudatos `WeakRefSubject` nulláról történő felépítéséig. Láttuk, hogyan teszi lehetővé a `WeakRef`, hogy az objektumokat a szemétgyűjtő eltávolítsa, még akkor is, ha „megfigyelik” őket, és hogyan biztosítja a `FinalizationRegistry` az automatizált takarítási mechanizmust, hogy a megfigyelői listánk tiszta maradjon.
Azonban a nagy erő nagy felelősséggel jár. Ezek haladó funkciók, amelyek nem determinisztikus természete gondos mérlegelést igényel. Nem helyettesítik a jó alkalmazástervezést és a gondos életciklus-kezelést. De amikor a megfelelő problémákra alkalmazzák őket – mint például a hosszú életű szolgáltatások és a rövid életű komponensek közötti kommunikáció kezelése –, a WeakRef Observer minta kivételesen hatékony technika. Ennek elsajátításával robusztusabb, hatékonyabb és skálázhatóbb JavaScript alkalmazásokat írhatsz, amelyek készen állnak a modern, dinamikus web követelményeinek való megfelelésre.